-
-
Notifications
You must be signed in to change notification settings - Fork 729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DFC Orders] Sync remote products when order cycle opens #13167
base: master
Are you sure you want to change the base?
[DFC Orders] Sync remote products when order cycle opens #13167
Conversation
4d8cd8c
to
f665516
Compare
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, all going in the right direction. That's basically it. You just need to check for other open order cycles of the product, maybe. Or maybe we just skip that until it becomes a problem? I'm worried that multiple enterprises could have different overlapping OCs and then this will never get triggered. 🤷 But maybe that's a problem we don't have yet. 😉
ef5ce63
to
2d4c5d1
Compare
def mark_as_opened(order_cycles) | ||
now = Time.zone.now | ||
order_cycles.update_all(opened_at: now, updated_at: now) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a spec for this in a later commit.
06088fd
to
42ddb61
Compare
I considered pre-loading the variant and product with includes, But can't do that here because it's a polymorphic relationship.
It was probably better to be explicit at each test, but this one is always repeated and approaches the line length, so I wanted to just define it once at the top.
There might be a delay before it gets sent, so it's better to record the time the event occurred at. It would have been simpler to just add it to the data hash, but I felt it was an important detail for an event and should be at the top level along with event name. In the case of order cycle opening, this is the same as opened_at. I've included this in the payload for clarity too.
And re-organise specs. TOFIX: concurrency test now fails. why?
Being based on the DB value should be more robust. This prevents an order cycle from being "opened" when it's already open. But note that the order cycle can become "unopened" (see OrderCycle#reset_opened_at). Nice to see the database query count drop, but I must confess I don't know why!
and in that case, might as well define it once at the top. But it didn't help with spec failures.. see next commit
I guess we lose some of the detail in the db.
166ed5c
to
65d8c56
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. I lost some focus towards the end because it's after 5pm. But it's mostly good. I think there are a few small things to improve and then it's good.
before do | ||
user.oidc_account.update!(token: allow_token_for(email: user.email)) | ||
end | ||
|
||
# should we move any parts of importing to a separate class, and test it separately? | ||
it "synchronises products from a FDC catalog", vcr: true do | ||
user.update!(oidc_account: build(:testdfc_account)) | ||
# One product is existing in OFN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that you want to create(:testdfc_user)
. The allow_token_for
method from the AuthorizationHelper is to create a valid access token locally for the OFN DFC API. If you use VCR to record a real interaction then you will have real tokens and don't need that helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, thanks.
I have to admit I cheated and copy/pasted the VCR fixture from elsewhere. I can't re-record it because I'm forbidden. Can you please guide me how to authenticate it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how to best document this. You need a refresh token. So the best way I found is to run your local dev server, connect a user to the testdfc user OIDC account and then get the token:
./bin/rails runner 'puts "OPENID_REFRESH_TOKEN=\"#{OidcAccount.last.refresh_token}\""'
You then paste that line into your .env.test.local
file. You also need the OIDC creds in your env:
OPENID_APP_ID="coopcircuits"
OPENID_APP_SECRET="..."
OPENID_REFRESH_TOKEN="eyJh..."
I think that we should put these instructions as a comment in .env.test
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! You've told me this before and I forgot sorry. I'll push up a suggested .env.test change.
app/jobs/open_order_cycle_job.rb
Outdated
class OpenOrderCycleJob < ApplicationJob | ||
def perform(order_cycle_id) | ||
order_cycle = OrderCycle.find(order_cycle_id) | ||
sync_remote_variants(order_cycle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagined the syncing in a new job. Then that logic is isolated and can be triggered via buttons or at other points, too. But I think that you wanted to make sure it finishes before the order cycle is seen as open. Fair enough. There is perform_now
though which can be handy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it might be useful in the future but I didn't think it was needed yet.
Open Order Cycle still needs its own job here, so that the syncing (which has to happen synchronously) doesn't delay the opening of other order cycles.
The naming is confusingly similar to the existing OrderCycleOpenedJob, but let's see if we can improve that one..
app/jobs/order_cycle_opened_job.rb
Outdated
# Currently, an order cycle is considered open in the shopfront when orders_open_at >= now. | ||
# But now there are some pre-conditions for opening an order cycle, so we would like to change that. | ||
# When it changes, this would become OrderCycleOpeningJob, with the responsibility of opening each | ||
# order cycle by setting opened_at = now. | ||
class OrderCycleOpenedJob < ApplicationJob |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is now confusing because it happens after orders_open_at
but before opened_at
. So the OC hasn't really opened yet. Most of my ideas are still confusing. Maybe FindOrderCyclesToOpenJob
? 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm not sure either.. how about:
EnqueueOpeningOrderCyclesJob
ScheduleOpeningOrderCyclesJob
ProcessOpeningOrderCyclesJob
OpeningOrderCyclesJob
ScheduleOrderCyclesToOpenJob
I think I like the last one, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the first and the last. I would go for Enqueue
because it doesn't schedule for a future time. It just does it straight after. It could even happen in parallel because Sidekiq uses several threads. It's really hard to write though. Maybe trigger is a better word? In that case we could also say TriggerOrderCycleOpeningJob
. But then it's better the re-use the other job name: TriggerOpenOrderCycleJob
. I also like the ToOpen
because it refers to the action to do. My favourites now are:
TriggerOpeningOrderCyclesJob
TriggerOpenOrderCycleJob
TriggerOrderCyclesToOpenJob
The last one is my favourite. But I would also be happy about ScheduleOrderCyclesToOpenJob
if you prefer the schedule part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Trigger sounds good, I'll go ahead with TriggerOrderCyclesToOpenJob
.to change { order_cycle.opened_at } | ||
|
||
expect(order_cycle.opened_at).to be_within(1).of(now) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I thought that there was some way to convert the timestamp to the db precision. I can't remember it now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this was just the first solution I came across, and it worked :)
Another thought was to use to_i
when comparing.
Co-authored-by: Maikel <[email protected]>
> The order cycle itself is not changed. It's just that time passed and it's now considered open/closed.
@mkllnk thanks for your feedback. I've made some changes and have a couple of questions. |
2eb96bc
to
e2a871e
Compare
app/jobs/open_order_cycle_job.rb
Outdated
rescue ActiveRecord::RecordNotFound | ||
# do nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't really happen, should it? I'm wondering if we should report this to Bugsnag. Otherwise there may be a case when we are wondering: Why did the job not do its job? And we won't have any record of that.
I think that this only happens if the syncing takes so long that five minutes passed and the job gets scheduled again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, we shouldn't suppress the error.
Because Sidekiq retries jobs that raise an exception (for about 20 days), I chose to suppress the error.
I also don't want to set sidekiq_options retries: 0
, in case there's some other error, and maybe it should be retried (although maybe only for 10 mins). Sidekiq doesn't seem to have a way to configure retries based on a condition.
So.. I'm thinking to set sidekiq_options retry_for: 10.minutes
and not have any error handling. What do you think?
And document how to run it.
e2a871e
to
0fa71e2
Compare
After 10 minutes, I'd consider that it failed to open the order cycle. Who would want their products to sync, or get a notification at a random time during the order cycle? Best viewed with whitespace ignored.
This name better reflects what it's doing. As this job is scheduled automatically by Sidekiq, I think there shouldn't be any jobs with the old name in redis. So I didn't bother keeping a placholder for the old name.
f2a0a6d
to
6d5aaa1
Compare
ℹ️ Funded Feature. Please track ALL ASSOCIATED WORK under the associated tracking code
#11678 DFC Orders
What? Why?
When an order cycle opens, any remote (DFC) products will get re-imported to ensure the product is up to date. This is because the product on OFN is considered a cache, or a view of, the "real" product.
There's currently no record or notification of this happening for the enterprise managers or OC co-ordinator.
This required some changes to the way we enqueue and execute the scheduled jobs for opening an order cycle.
There is also a slight change to the Webhook
order_cycle.opened
(https://ofn-user-guide.gitbook.io/ofn-api-handbook/webhooks/event-types). Theat
timestamp now more accurately reflects the time the event occurred, rather than when the webhook was sent. It's such a nuanced change I don't think it's necessary to announce.What should we test?
Release notes
Changelog Category (reviewers may add a label for the release notes):
The title of the pull request will be included in the release notes.